Introduction to XML in Flash
by senocular
Example: Searching XML
Below is an XML file that contains a few posts
from the Best of Kirupa.com forum on KirupaForum.com.
These are a bunch of posts (ok, only ten but
I didn't feel like adding that many) that no
one probably wants to read all at once, especially
when they're looking for something in particular.
It would be nice to just pull out only the post
that relates to whatever it is you are trying
to find. For that, a search is required.
Depending on the XML being searched, you will
need to decide how you want to approach your
search. Either way you look at it, searching
XML means going through all elements of that
XML (or all that are of interest) and checking
its content for whatever is being searched.
The XML:
bestofposts.xml
It starts off looking like (the entire file
is not shown here):
- <bestof url="http://www.kirupaforum.com/forums/forumdisplay.php?f=12">
- <post url="http://www.kirupaforum.com/forums/showthread.php?t=43216">
- <title>xml
menu DONE :)</title>
- <author>hga77</author>
- <message>
- <![CDATA[Happy
New Year kirupa....Hope all you ppl
had a great year and didnt stick to
flash too much
-
- Anyways I just
managed to finish the xml menu i've
been working on...
-
- I've attached
it here incase any1 needs something
like this. Use it as you like. Let me
know if you make any additions to it
-
- UPDATE(04/2004):
you can find v2 for this menu on page
3 post #31
- UPDATE(07/2004):
you can find v3 for this menu on page
15 post #221]]>
- </message>
- </post>
- .
- .
- .
Each post following this one maintains the
same format. As you can probably tell, we should
be able to get along fine just using loops.
Two will be needed; one nested within the other.
One will loop through each post while the other
will loop through each element within each post
checking it for a match with whatever was searched.
The final result is:
[
search for terms within the bestof xml ]
Preparing the Flash File
The file consists of 2 parts, the display area
and the search area. The display area shows
the XML (in a text field called results_txt),
or at least the XML elements that were found
in any given search. The search area shows the
search input field, some options and the go
button.
Because the XML is external and because it
has to be loaded to be searched, the search
area is self-contained within a movie clip and
hidden from view until the loading of the XML
is complete. This prevents errors that might
have occurred if someone were to search for
something in XML that just isn't there.
[
search_fields movie clip symbol ]
When the go button is pressed, the value of
the find text field (query_txt) will be sent
to a function which will find that value in
the loaded XML within any of the elements whose
checkbox is selected. Note that each checkbox
above says title because their names are dynamically
created at runtime (each of the three are the
same symbol, each relating to a different element
as specified in code).
ActionScript
There are a couple of processes working together
to complete the search provided within this
example. You have a function to determine what
is being searched (where). There's the actual
search operation. There's a function used to
put the nodes found to have the query in the
text field, and then there's a function used
to highlight those results in the text displayed.
There's also a custom scrollbar for the search
results. I won't go into detail about the scrollbar
here, the graphics nor the scripting. It's just
like any other custom scrollbar used to scroll
content. If you're interested in how its made,
you can study the code from the Flash source
file. Here, the concentration will be on working
with the XML aspects of the example. It will
pop up in some other examples as well.
As usual, we'll start with the XML instance
used for this example since everything starts
with and revolves around its existence.
- var posts_xml = new XML();
- posts_xml.ignoreWhite = true;
- posts_xml.onLoad = function(success){
- if (success){
- search_fields._visible = true;
- }else results_txt.text = "Error loading XML";
- }
- search_fields._visible = false;
- posts_xml.load("bestofposts.xml");
The posts_xml variable here represents the
XML instance. It immediately loads the external
file "bestofposts.xml" right after
the search_fields movie clip has its _visible
set to false. Once the loads, in the onLoad
event, assuming the loading was successful,
search_fields is made visible again and the
user can begin to search the data. Otherwise,
an error is given in the results_txt text field
and you're basically out of luck.
With search_fields visible, the search function
is active. This is initiated by the go button
on the right. The go button contains the list
of commands (functions) that were mentioned
earlier. When pressed and released, it:
- checks for a valid search query (the example
limits searches to terms at least 3 characters
in length).
- determines which parts of the XML is to
be searched (as specified with the checkboxes).
- performs the search in the XML with the
query provided.
- displays the results in the results text
field.
- highlights the search term within the results.
That results in the following for the button's
onRelease event:
- search_fields.search_btn.onRelease = function(){
- if (search_fields.query_txt.text.length < 3){
- results_txt.text = "Please use a search term with 3 or more characters.";
- return (0);
- }
-
- var searchElements = ElementsToSearch();
-
- var nodesWithQuery = SearchXML(
- posts_xml.firstChild.childNodes,
- search_fields.query_txt.text,
- searchElements
- );
-
- if (nodesWithQuery.length){
- DisplayNodes(
- nodesWithQuery,
- results_txt
- );
- }else{
- results_txt.text = "No results for "+search_fields.query_txt.text+".";
- return (0);
- }
-
- HighlightOccurences(
- search_fields.query_txt.text,
- results_txt,
- search_highlight
- );
- }
First the check for the valid search query.
Since we're only checking for queries with lengths
of 3 or greater, this is handled with a simple
if condition.
- if (search_fields.query_txt.text.length
< 3){
- results_txt.text
= "Please
use a search term with 3 or more characters.";
- return (0);
- }
If the search query (search_fields.query_txt.text)
is not at least three characters in length,
an error message is displayed and a return statement
exits the onRelease preventing anything else
from being run.
Next, a function is used to determine which
elements within the XML need to be searched.
var searchElements
= ElementsToSearch();
ElementsToSearch is defined as:
- ElementsToSearch = function(){
- var childElementsToSearch = [];
- if (search_fields.title_check.checked){
- childElementsToSearch.push("title");
- }
- if (search_fields.author_check.checked){
- childElementsToSearch.push("author");
- }
- if (search_fields.message_check.checked){
- childElementsToSearch.push("message");
- }
- return childElementsToSearch;
- }
What this does is is runs through the checkboxes
within the search_fields movie clip, adding
the associated element node name to an array
if the checkbox is checked. This array is then
returned and assigned to searchElements to be
used within the search function.
The search is what comes next. The function
handling the search is called SearchXML. It
takes an array of elements to be searched (a
childNodes array), what is to be searched and
an array of elements within the elements searched...
to be searched.
- var nodesWithQuery = SearchXML(
- posts_xml.firstChild.childNodes,
- search_fields.query_txt.text,
- searchElements
- );
posts_xml.firstChild.childNodes represents
the list of post elements within posts_xml.
Each of those post elements contains title,
author and message elements. Which of those
to be searched is dictated by the searchElements
array originally obtained by the ElementsToSearch
function. As for the SearchXML function, it
comprises of the following:
- SearchXML = function(nodes, query, useChildElements){
- var results = [];
- for (var i=0; i<nodes.length; i++){
- for (var j=0; j<nodes[i].childNodes.length; j++){
- currNode = nodes[i].childNodes[j];
- if (useChildElements.contains
(currNode.nodeName)){
- if
(currNode.firstChild.nodeValue.contains
(query)){
- results.push(nodes[i]);
- break;
- }
- }
- }
- }
- return results;
- }
Using 2 for loops, one nested in the other,
this takes and places all post elements that
contain the query in a returned results variable.
The first loop which uses the i variable as
an iterator loops through all post elements
passed in as the nodes variable (set to posts_xml.firstChild.childNodes
when called). The second loop, using j, cycles
through all of each of the child nodes within
each of those elements; in this case, title,
author and message.
[ nested for loops for post
elements ]
Inside those loops, as looping through each
post element, the useChildElements array (searchElements)
is checked to see if that particular node is
to be searched by seeing if its nodeName exists
within it. If so, the following if statement
does the actual searching through that element's
text Node for the query. If that exists, then
the post element is added to the results array
and the inner for loop (j) is broken out of
in order to immediately begin searching the
next post element. When all post elements are
searched, the results array is returned and
assigned to the nodesWithQuery variable back
in the go button's onRelease event.
You may not recognize the two functions (though
with a similar name) used to check the useChildElements
array and the text node for the search query.
These are the Array and String contains methods.
You probably don't recognize them because they're
custom methods defined to work with the String
and Array objects. They are defined within the
movie as:
- String.prototype.contains = function(searchString){
- return (this.indexOf(searchString) != -1);
- }
- Array.prototype.contains = function(searchValue){
- var i = this.length;
- while(i--) if (this[i] == searchValue) return true;
- return false;
- }
They're simple enough that they could be written
directly within the Search XML function, but
separating them like this keeps SearchXML cleaner
and more understandable. Its much easier to
tell what's going on when you read if string
contains word versus if string indexof
word != -1 when determining when a string
(word) exists within another. (If you're unfamiliar
with indexof for Strings, it returns the location
of one string within another. If the string
does not exist within the other string, -1 is
returned.)
Getting back to the original go button's onRelease,
the next item up for bid is the displaying of
the results within the results_txt text field.
- if (nodesWithQuery.length){
- DisplayNodes(
- nodesWithQuery,
- results_txt
- );
- }else{
- results_txt.text = "No results for "+search_fields.query_txt.text+".";
- return (0);
- }
An if statement is needed here because we need
to check to see whether or not there are any
results to display. If there are, call DisplayNodes
to put the nodesWithQuery elements into results_txt.
If not, then display an error message there
instead. DisplayNodes is defined as:
- DisplayNodes = function(nodes, field_txt){
- field_txt.htmlText = "";
- var entry;
- var separator = "<br>_______________________<br><br>";
- for (var i=0; i<nodes.length; i++){
- entry = "";
- entry += "<b>"+ nodes[i].childNodes[0].firstChild.nodeValue +"</b>";
- entry += " by: "+ nodes[i].childNodes[1].firstChild.nodeValue;
- entry += "<br>"+ nodes[i].childNodes[2].firstChild.nodeValue;
- if (nodes[i].attributes.url.length){
- entry += "<br><a href='" + nodes[i].attributes.url;
- entry += "'><font color='#0000FF'>Read more...</font></a>";
- }
- field_txt.htmlText += entry + separator;
- }
- }
DisplayNodes simply formats the XML to be displayed
in the text field (using HTML).
At this point the search is complete. All nodes
with the query have been found and displayed
in the results_txt. To make the results a little
more convenient, though, I've added a little
function that goes through and highlights the
search terms within the results. This is the
HighlightOccurences function.
- HighlightOccurences(
- search_fields.query_txt.text,
- results_txt,
- search_highlight
- );
It's defined as:
- search_highlight = new TextFormat();
- search_highlight.color = 0xFF0000;
- search_highlight.italic = true;
-
- HighlightOccurences = function(str, field_txt, format){
- if (!str.length) return (0);
- var start = field_txt.text.indexOf(str);
- var end = start + str.length;
- while (start != -1){
- field_txt.setTextFormat(start, end, search_highlight);
- start = field_txt.text.indexOf(str, end);
- end = start + str.length;
- }
- }
It uses a TextFormat instance to make all the
str (query) instances within the field_txt text
field (results_txt) turn red - or at least whatever
the format passed specifies. And that format
you can see defined above the function does
in fact set text red.
|